package com.example.websocket.ws;

import com.example.websocket.pojo.ws.Message;
import com.example.websocket.pojo.ws.ResultMessage;
import com.example.websocket.utils.MessageUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static com.example.websocket.constants.Constants.*;

/**
 * @ author 韩
 * time: 2024/3/6 8:09
 */
@ServerEndpoint(value = "/ptop",configurator = GetHttpSessionConfigurator.class)
@Slf4j
@Component
public class PtoP {

    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        PtoP.applicationContext = applicationContext;
    }
    // 用来存储每一个客户端对象对应的ChatEndpoint对象
    private static final Map<String,PtoP> onlineUsers = new ConcurrentHashMap<>();

    //声明session对象，通过该对象可以发送消息给指定的用户
    private Session session;
    //声明一个HttpSession对象，我们之前再HttpSession对象中存储了用户名
    private HttpSession httpSession;

    private StringRedisTemplate redisTemplate;
    private String username;

    // 专属于对象的一个boolean变量 聊天室的websocket不需要考虑 这个问题因为他的key是固定的 可以在onOpen里直接初始化了 但是这个没有toName
    // 必须等到后端发过来 然后从onMessage中进行初始化,让前端直接发一条消息即可
    // 这个boolean变量都不需要了 定义一个init的key 判断一下 如果相等直接初始化即可 算了 直接发toName就好 然后json反序列化时候会报错 异常处理中初始化

    /**
     * 建立websocket连接后，被调用
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //将局部的session对象赋值给成员session
        this.session = session;
        //获取httpSession对象
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;

        //从httpSession对象中获取用户名
        username = (String) httpSession.getAttribute("username");

        //将当前对象存储到容器中
        onlineUsers.put(username,this);
        // 把所有的redis里的消息遍历 发送到前端
        redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
//        redisTemplate.opsForValue().set("age","123");


    }


    @OnMessage
    //接收到客户端发送的数据时被调用
    public void onMessage(String message,Session session) {
        boolean flag = true;
        // 修改了一下多路复用的判断手法，普通字符串肯定没有"
        if (message.contains("\"")) {
            try {
                //将message转换成message对象
                ObjectMapper mapper = new ObjectMapper();
                Message mess = mapper.readValue(message, Message.class);
            } catch (Exception e) {
                flag = false;
            }

            if (flag) {
                //将message转换成message对象
                ObjectMapper mapper = new ObjectMapper();
                Message mess = null;
                try {
                    mess = mapper.readValue(message, Message.class);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
                //获取要将数据发送给的用户
                String toName = mess.getToName();
                //获取消息数据
                String data = mess.getMessage();
                //然后就正常编写发送消息的逻辑
                //获取推送给指定用户的消息格式的数据
                long currentTime = System.currentTimeMillis();
                String resultMessage = MessageUtils.getMessage(username,toName, data, currentTime);
                //为了解决一个问题 当AtoB和BtoA两个数据库问题 我们这里存入的时候直接先排个序

                redisTemplate.opsForZSet().add((
                                username.compareTo(toName) > 0 ? username : toName)+ LINK_KEY + (username.compareTo(toName) < 0 ? username : toName),
                        resultMessage,currentTime);
                redisTemplate.expire((
                                username.compareTo(toName) > 0 ? username : toName)+ LINK_KEY + (username.compareTo(toName) < 0 ? username : toName)
                        , 7, TimeUnit.DAYS);
                //发送数据
                try {
                    if (onlineUsers.get(toName) != null) {
                        onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
                    }
                    this.session.getBasicRemote().sendText(resultMessage);

                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {

                //将传过来的message转换成ResultMessage对象
                ObjectMapper mapper = new ObjectMapper();
                ResultMessage mess = null;
                try {
                    mess = mapper.readValue(message, ResultMessage.class);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
                String toName = mess.getToName();
                String key = (username.compareTo(toName) > 0 ? username : toName)+
                        LINK_KEY + (username.compareTo(toName) < 0 ? username : toName);
                redisTemplate.opsForZSet().remove(key, message);

                // 直接把toName的清除即可，发送一段特殊的消息就发时间戳
                try {
                    System.out.println(mess.getTime());
                    onlineUsers.get(toName).session.getBasicRemote().sendText(String.valueOf(mess.getTime()));
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }


        } else {
            System.out.println("第一次发的消息 初始化");
            String key = (username.compareTo(message) > 0 ? username : message) + LINK_KEY + (username.compareTo(message) < 0 ? username : message);
            Set range = redisTemplate.opsForZSet().range(key, 0, -1);
            System.out.println(key);
            System.out.println(range);
            range.forEach((v) -> {
                try {
                    this.session.getBasicRemote().sendText((String) v);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            });
        }

    }

    @OnClose
    //连接关闭时被调用
    public void onClose(Session session) {
        //从容器中删除指定的用户
        onlineUsers.remove(username);
    }
}
